package com.hn.securityjwt.config;

import com.hn.securityjwt.component.JwtAuthenticationTokenFilter;
import com.hn.securityjwt.component.RestAuthenticationEntryPoint;
import com.hn.securityjwt.component.RestfulAccessDeniedHandler;
import com.hn.securityjwt.entity.AdminUserDetails;
import com.hn.securityjwt.entity.UmsAdmin;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;

import java.util.ArrayList;
import java.util.List;


/**
 * SpringSecurity的配置
 * Created by macro on 2018/4/26.
 */
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled=true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private RestfulAccessDeniedHandler restfulAccessDeniedHandler;
    @Autowired
    private RestAuthenticationEntryPoint restAuthenticationEntryPoint;

    //拦截配合
    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        httpSecurity.csrf()// 由于使用的是JWT，我们这里不需要csrf
                .disable()
                .sessionManagement()// 基于token，所以不需要session
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authorizeRequests()
                .antMatchers(HttpMethod.GET,
                        "/",
                        "/*.html",
                        "/favicon.ico",
                        "/**/*.html",
                        "/**/*.css",
                        "/**/*.js"
                ).permitAll()// 允许对于网站静态资源的无授权访问
                .antMatchers(HttpMethod.OPTIONS).permitAll()//跨域请求会先进行一次options请求
                .antMatchers("/login").permitAll()// 对登录允许匿名访问
                .antMatchers("/admin").hasRole("ADMIN")//拥有admin角色才能访问
                .antMatchers("/user").hasRole("USER")//拥有USER角色才能访问
                .antMatchers("/hello").hasAnyRole("ADMIN", "USER")//拥有ADMIN或USER角色才能访问
                //.antMatchers("/**").permitAll()//测试时全部匿名访问
                .anyRequest().authenticated();// 除上面外的所有请求全部需要鉴权认证


        // 禁用缓存
        httpSecurity.headers().cacheControl();
        // 添加JWT filter
        httpSecurity.addFilterBefore(jwtAuthenticationTokenFilter(), UsernamePasswordAuthenticationFilter.class);
        //添加自定义未授权和未登录结果返回
        httpSecurity.exceptionHandling()
                .accessDeniedHandler(restfulAccessDeniedHandler)
                .authenticationEntryPoint(restAuthenticationEntryPoint);
    }

    //认证授权
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService())
                .passwordEncoder(passwordEncoder());
    }


    //密码加密
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }



    //这里相当于实现UserDetailsService的接口,在登陆的时候调用,接口参数username
    @Bean
    public UserDetailsService userDetailsService() {
        //这里是java8的写法,返回一个方法
        //就是相当于实现UserDetailsService接口
        //最终返回AdminUserDetails
        return username -> {
            //这里应该通过数据,根据用户名查询用户信息,校验用户名密码是否正确
            //测试用密码
            String password = passwordEncoder().encode("123456");
            UmsAdmin admin = new UmsAdmin(username, password);
            if (admin != null) {
                //这里应该通过数据库,根据用户查询用户角色权限信息
                List<String> roles = new ArrayList<String>();
                //admin用户授予ADMIN的角色,添加角色需时要添加'ROLE_'前缀,因为源码判断会先加前缀在判断
                if("admin".equals(username)){
                    roles.add("ROLE_ADMIN");
                }else{//普通用户授予USER角色
                    roles.add("ROLE_USER");
                }

                return new AdminUserDetails(admin, roles);
            }
            throw new UsernameNotFoundException("用户名或密码错误");
        };
    }

    @Bean
    public JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter(){
        return new JwtAuthenticationTokenFilter();
    }

    /**
     * 允许跨域调用的过滤器
     */
    @Bean
    public CorsFilter corsFilter() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration config = new CorsConfiguration();
        config.addAllowedOrigin("*");
        config.setAllowCredentials(true);
        config.addAllowedHeader("*");
        config.addAllowedMethod("*");
        source.registerCorsConfiguration("/**", config);
        FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
        bean.setOrder(0);
        return new CorsFilter(source);
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

}
